package com.maozzi.api.auth;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.maozzi.api.auth.annotation.Login;
import com.maozzi.api.auth.constant.AuthConstant;
import com.maozzi.api.auth.model.UserInfo;
import com.maozzi.api.auth.properties.AuthProperties;
import com.maozzi.api.auth.util.SsoUtil;
import com.maozzi.api.redis.RedisService;
import com.maozzi.common.enums.BizCodeEnum;
import com.maozzi.common.exception.BizException;
import com.maozzi.common.model.ResultVO;
import com.maozzi.common.util.AesUtil;
import com.maozzi.common.util.HttpUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpHeaders;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 登录拦截器
 *
 * @author maozi
 */
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 主机
     */
    private final String host;

    /**
     * 查询用户信息URL
     */
    private final String userInfoUrl;

    /**
     * 查询acl用户信息URL
     */
    private final String aclUserInfoUrl;

    private final static Gson GSON = new Gson();

    @Resource
    private RedisService redisService;

    public LoginInterceptor(AuthProperties authProperties) {
        this.host = authProperties.getHost();
        this.userInfoUrl = authProperties.getUserInfoUrl();
        this.aclUserInfoUrl = authProperties.getAclUserInfoUrl();
    }

    @Override
    public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
        String url = request.getRequestURI();
        if (AuthConstant.ERROR_URLS.contains(url)
                || userInfoUrl.equalsIgnoreCase(url)
                || aclUserInfoUrl.equalsIgnoreCase(url)
        ) {
            return true;
        }

        // 检查是否@login 注解
        boolean needLogin = this.checkAnnotation(handler);
        if (!needLogin) {
            return true;
        }

        // 检查是否携带token
        String token = this.getToken(request);
        if (!StringUtils.hasText(token)) {
            // 未登录
            throw new BizException(BizCodeEnum.NEED_LOGIN);
        }

        // 根据token查询用户info
        UserInfo userInfo = this.getUserInfo(token);
        if (ObjectUtils.isEmpty(userInfo)) {
            throw new BizException(BizCodeEnum.NEED_LOGIN);
        }

        SsoUtil.set(userInfo);

        return true;
    }

    /**
     * 检查是否@login 注解
     *
     * @param handler h
     * @return true-需要;false-不需要登录
     */
    private boolean checkAnnotation(Object handler) {
        Login annotation = null;
        // 如果是SpringMVC请求
        if (handler instanceof HandlerMethod handlerMethod) {
            annotation = handlerMethod.getMethodAnnotation(Login.class);
        }
        if (Objects.isNull(annotation)) {
            annotation = handler.getClass().getAnnotation(Login.class);
        }
        if (Objects.isNull(annotation)) {
            return false;
        }

        return annotation.require();
    }

    /**
     * 获取 header auth token
     *
     * @param request 请求
     * @return 返回
     */
    private String getToken(HttpServletRequest request) {
        String token = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (Objects.isNull(token) || token.isBlank()) {
            return null;
        }

        return token.replace("Bearer", "").replaceAll(" ", "");
    }

    /**
     * 查询用户数据
     *
     * @param token 令牌
     * @return 返回
     */
    private UserInfo getUserInfo(String token) {
        // 先检查redis 缓存，是否过期，如果有则未过期
        String s = redisService.get(AuthConstant.LOGIN_KEY_PREFIX + token);
        if (Objects.isNull(s) || s.isBlank()) {
            return null;
        }

        Map<String, String> headers = new HashMap<>(1);

        headers.put("X-Requested-With", "XMLHttpRequest");
        Map<String, String> params = new HashMap<>(3);
        String timestamp = String.valueOf(System.currentTimeMillis());
        params.put("timestamp", timestamp);
        params.put("sign", AesUtil.encrypt(token, timestamp));
        params.put("token", token);
        String response = HttpUtil.sendGet(host + userInfoUrl, headers, params);
        ResultVO<UserInfo> resultVO = GSON.fromJson(response, new TypeToken<>() {
        });

        return ObjectUtils.isEmpty(resultVO) ? null : resultVO.getData();
    }

}
